#include "Application.hpp"

Application::Application(const char *title, int x, int y, int w, int h, Uint32 flags)
{
    this->win = SDL_CreateWindow(title, x, y, w, h, flags);
    this->renderer = SDL_CreateRenderer(win, -1, 0);
    this->scenes.push(new MainMenuScene(w,h));
}

Application::~Application()
{
    SDL_DestroyRenderer(this->renderer);
    SDL_DestroyWindow(this->win);
    while(!this->scenes.empty()){
        delete this->scenes.top();
        this->scenes.pop();
    }
}

int Application::exec()
{
    SDL_Event evt;
    float delay = 0;
    float delta = 0;
    float delta_revise = 0;
    float real_delta = 0;
    Timer fps_timer;

    while (!this->exited)
    {
        while (SDL_PollEvent(&evt))
        {
            if (evt.type == SDL_QUIT)
            {
                exited = true;
            }else if(evt.type == SDL_KEYDOWN){
                this->keyDown(evt.key);
            }
            if(!this->scenes.empty()){
                int res = this->scenes.top()->handleEvent(evt);
                if(res==-1){
                    delete this->scenes.top();
                    this->scenes.pop();
                }else if(res==-2){
                    this->exited=true;
                }
            }
        }
        delta = fps_timer.getElapsedSeconds();
        delay=this->msDelay-delta*1000.f;
        fps_timer.recordTimePoint();
        if(real_delta>0){
            this->update(real_delta);
            this->render(real_delta);

            delta_revise=0;
        }
        if(delay>0){
            SDL_Delay(delay);
            real_delta=this->msDelay/1000.f;
        }else{
            //超时补偿
            delta_revise-=delay;
            real_delta=delta;
        }

        // char buff[0xff];
        // SDL_snprintf(buff,0xff,"app delta:%f revise:%f delay:%f",delta,delta_revise,delay);
        // SDL_SetWindowTitle(this->win,buff);
    }
    return 0;
}

void Application::setFps(int fps)
{
    if (fps <= 1)
    {
        fps = 1;
    }
    if(fps>=1000){
        fps = 1000;
    }
    this->msDelay = 1000 / (float)fps;
}

bool Application::init()
{
    if(Static::init()){
        Component::setDefaultFont(Static::default_font);
        return true;
    }
    return false;
}

void Application::quit()
{
    Static::quit();
}

void Application::showFps(bool value)
{
    this->isShowFps = value;
}

void Application::render(float delta)
{
    SDL_SetRenderDrawColor(this->renderer, 0xff, 0xff, 0xff, 0xff);
    SDL_RenderClear(this->renderer);
    SDL_Rect rc{0, 0, 100, 100};
    static Uint32 begin = SDL_GetPerformanceCounter();
    static Uint32 end = SDL_GetPerformanceCounter();
    static Uint32 fps = 100;
    if(!this->scenes.empty()){
        this->scenes.top()->render(this->renderer);
    }
    if (this->isShowFps)
    {
        end=SDL_GetPerformanceCounter();
        float delta_fps = (end - begin) / (float)SDL_GetPerformanceFrequency();
        if (delta_fps >= 1)
        {
            fps = 1/delta;
            begin = SDL_GetPerformanceCounter();
        }
        char buff[0xff];
        SDL_snprintf(buff, 0xff, "fps:%d",fps);
        TTF_SizeUTF8(Static::default_font, buff, &rc.w, &rc.h);
        SDL_Surface *sur_text = TTF_RenderUTF8_Blended(Static::default_font, buff, {0, 0xff, 0, 0xff});
        SDL_Texture *tex_text = SDL_CreateTextureFromSurface(this->renderer, sur_text);
        SDL_RenderCopy(this->renderer, tex_text, nullptr, &rc);
        SDL_DestroyTexture(tex_text);
        SDL_FreeSurface(sur_text);
    }
    SDL_RenderPresent(this->renderer);
}

int Application::update(float delta)
{
    int res=0;
    if(!this->scenes.empty()){
        res=this->scenes.top()->update(delta);
    }
    return res;
}

void Application::keyDown(SDL_KeyboardEvent &evt)
{
    switch (evt.keysym.sym)
    {
    case SDLK_ESCAPE:
        exited = true;
        break;
    default:
        break;
    }
}
